/**
  ******************************************************************************
  * File Name          : edp_app.c
  * Description        : This file provides code for the configuration
  *                      of the edp app instances.
  ******************************************************************************
  * @attention
  ******************************************************************************
  */
#include <string.h>
#include "tskcfg.h"
#include "network.h"
#include "route.h"

TaskHandle_t xNetWorkRecv_Thd = NULL;
StaticTask_t xNetWorkRecv_Tcb;
StackType_t  xNetWorkRecv_Stk[ NETWORK_RECV_STACK_SIZE ];

TaskHandle_t xNetWorkTran_Thd = NULL;
StaticTask_t xNetWorkTran_Tcb;
StackType_t  xNetWorkTran_Stk[ NETWORK_TRAN_STACK_SIZE ];

QueueHandle_t        Network_Acks_qhd = NULL;
static StaticQueue_t Network_Acks_stru;
static uint8_t       Network_Acks_area[NETWORK_ACKS_QUE_LENGTH  * NETWORK_ACKS_QUE_ITEMSIZE];

char Network_Subscribe_buffer[NETWORK_SUBSCRIBE_BUFFER_MAXLEN];

char Network_Pub_topic[NETWORK_PUB_TOPIC_BUFFER_MAXLEN];
char Network_Sub_topic[NETWORK_SUB_TOPIC_BUFFER_MAXLEN];

char Network_Sub_payload[NETWORK_SUB_PAYLOAD_BUFFER_MAXLEN];
char Network_Pub_payload[NETWORK_PUB_PAYLOAD_BUFFER_MAXLEN];

static const cJSON_Hooks cJSONHooks = 
{
  .malloc_fn = pvPortMalloc,
  .free_fn   = vPortFree,
};

MQTTContext_t MQTT_Context;

static void mqtt_resprint(MQTTStatus_t res)
{
  switch(res)
  {
    case MQTTSuccess:
      printf("res:MQTTSuccess\r\n");
      break;
    
    case MQTTBadParameter:
      printf("res:MQTTBadParameter\r\n");
      break;
    
    case MQTTNoMemory:
      printf("res:MQTTNoMemory\r\n");
      break;
    
    case MQTTSendFailed:
      printf("res:MQTTSendFailed\r\n");
      break;
    
    case MQTTRecvFailed:
      printf("res:MQTTRecvFailed\r\n");
      break;
    
    case MQTTBadResponse:
      printf("res:MQTTBadResponse\r\n");
      break;
    
    case MQTTServerRefused:
      printf("res:MQTTServerRefused\r\n");
      break;
    
    case MQTTNoDataAvailable:
      printf("res:MQTTNoDataAvailable\r\n");
      break;
    
    case MQTTIllegalState:
      printf("res:MQTTIllegalState\r\n");
      break;
    
    case MQTTStateCollision:
      printf("res:MQTTStateCollision\r\n");
      break;
    
    case MQTTKeepAliveLose:
      printf("res:MQTTKeepAliveLose\r\n");
      break;
    
    default:
      printf("res:Unknow\r\n");
      break;
  }
}

static void network_error_handle(uint8_t err)
{
  uint8_t i;
  
  if(err == 0)
  {
    return;
  }
  
  while(1)
  {
    for(i=0; i<(err>>5); i++)
    {
      LED_MQTT_Data(LED_ON);
      vTaskDelay(250);
      LED_MQTT_Data(LED_OFF);
      vTaskDelay(250);
    }
    
    for(i=0; i<(err&0x1Fu); i++)
    {
      LED_MQTT_Connect(LED_ON);
      vTaskDelay(250);
      LED_MQTT_Connect(LED_OFF);
      vTaskDelay(250);
    }
  }
}

void network_base_init(void)
{
  cJSON_InitHooks(&cJSONHooks);
  
  Network_TransportLayerInit();
  
  MQTT_Init(&MQTT_Context, &Network_TransportInterface, Network_GetTimestamp);
}

void network_base_deinit(void)
{
  Cat1_BspDeinit();
  
  LED_MQTT_Connect(LED_OFF);
  LED_MQTT_Data(LED_ON);
}

static void network_hardware_init(void)
{
  network_error_handle(Cat1_StaticConfig());
}

static uint8_t network_connect(void)
{
	MQTTStatus_t err;
	MQTTConnectInfo_t cinfo;
	bool sessionPresent;
  uint8_t retry, i;
  
  if((ROUTE_SELF_DID[0] == 0) || (ROUTE_SELF_TOKEN[0] == 0))
  {
    return (ERROR_PART4 + 11);
  }
  
  if(Route_ParseDID(ROUTE_SELF_DID, NULL) != 0)
  {
    return (ERROR_PART4 + 12);
  }
  
	cinfo.cleanSession = true;
	cinfo.keepAliveSeconds = 600;
  
	cinfo.pClientIdentifier = ROUTE_SELF_DID;
	cinfo.clientIdentifierLength = strlen(cinfo.pClientIdentifier);
  
	cinfo.pUserName = Route_str_pids[Route_DidToType(ROUTE_SELF_DID)];
	cinfo.userNameLength = strlen(cinfo.pUserName);
  
	cinfo.pPassword = ROUTE_SELF_TOKEN;
	cinfo.passwordLength = strlen(cinfo.pPassword);
  
  for(retry=0; retry<3; retry++)
  {
    err = MQTT_Connect(&MQTT_Context, &cinfo, NULL, &sessionPresent);
    if(err == MQTTSuccess)
    {
      return 0;
    }
    
    vTaskDelay(1500);
  }
  
  return (ERROR_PART4 + err);
}

static uint8_t network_subscribe(void)
{
	uint16_t packetId;
  uint8_t i, err;
	MQTTSubscribeInfo_t pinfo;
  Network_AcksType sack;
	
	packetId = MQTT_GetPacketId( &MQTT_Context );
	
	pinfo.qos = MQTTQoS1;
	snprintf(Network_Subscribe_buffer, NETWORK_SUBSCRIBE_BUFFER_MAXLEN, "$sys/"ROUTE_PID_GW"/%s/thing/#", ROUTE_SELF_DID);
  pinfo.pTopicFilter = Network_Subscribe_buffer;
	pinfo.topicFilterLength = strlen(pinfo.pTopicFilter);
  
  for(i=0; i<3; i++)
  {
    if(MQTT_Subscribe(&MQTT_Context, &pinfo, 1, packetId) != MQTTSuccess)
    {
      err = 11;
      vTaskDelay(1500);
      continue;
    }
    
    if(xQueueReceive(Network_Acks_qhd, &sack, 5000) == pdTRUE)
    {
      if((sack.type == MQTT_PACKET_TYPE_SUBACK) && (sack.packetId == packetId))
      {
        if(sack.result == MQTTSuccess)
        {
          return 0;
        }
        else
        {
          err = sack.result;
        }
      }
      else
      {
        err = 13;
      }
    }
    else
    {
      err = 12;
    }
    
    vTaskDelay(1500);
  }
  
  return (ERROR_PART5 + err);
}

static void netwrok_clear_all_buff(void)
{
  xStreamBufferReset(NetWork_StreamBuffer_sbhd);
  MQTT_ResetTrsBuffer();
  memset(Network_Subscribe_buffer, 0, NETWORK_SUBSCRIBE_BUFFER_MAXLEN);
  memset(Network_Pub_topic,   0, NETWORK_PUB_TOPIC_BUFFER_MAXLEN);
  memset(Network_Sub_topic,   0, NETWORK_SUB_TOPIC_BUFFER_MAXLEN);
  memset(Network_Sub_payload, 0, NETWORK_SUB_PAYLOAD_BUFFER_MAXLEN);
  memset(Network_Pub_payload, 0, NETWORK_PUB_PAYLOAD_BUFFER_MAXLEN);
}

static void network_upline(void)
{
  uint8_t retry, err;
  
  for(retry=0; retry<4; retry++)
  {
    vTaskSuspend(xNetWorkRecv_Thd);
    
    err = Cat1_WaitScoketALink();
    if(err == 0)
    {
      vTaskDelay(50);
      
      err = network_connect();
      if(err == 0)
      {
        vTaskResume(xNetWorkRecv_Thd);
        
        vTaskDelay(200);
        
        err = network_subscribe();
        if(err == 0)
        {
          LED_MQTT_Connect(LED_ON);
          
          SysDbgLog("NetWork Successful connection!\r\n");
          
          Route_gw_tonet_time();
          Route_gw_tonet_cat1info();
          
          return;
        }
      }
    }
    
    SysDbgLog("NetWork Connect fail! part:%u, err:%u, retry:%u\r\n", (err>>5), (err&0x1Fu), retry);
    netwrok_clear_all_buff();
    Cat1_ResetUp();
  }
  
  network_error_handle(err);
}

void vNetWorkReceive_Task( void * pvParameters )
{
  if(KeyNet() == 0)
  {
    vTaskDelay(150);
    
    if(KeyNet() == 0)
    {
      vTaskDelay(150);
      
      if(KeyNet() == 0)
      {
        network_base_deinit();
        
        while(1)
        {
          vTaskDelay(5000);
        }
      }
    }
  }
  
  network_base_init();
  
  network_hardware_init();
  
  Network_Acks_qhd = xQueueCreateStatic(NETWORK_ACKS_QUE_LENGTH, NETWORK_ACKS_QUE_ITEMSIZE,
	                                      Network_Acks_area, &Network_Acks_stru);
  
	xNetWorkTran_Thd = xTaskCreateStatic(vNetWorkTransmit_Task, "enttrs", NETWORK_TRAN_STACK_SIZE, (void *)1, \
                                       NETWORK_TRAN_TSK_PRIORITY,  xNetWorkTran_Stk,  &xNetWorkTran_Tcb);

	while(1)
	{
    if(MQTT_Context.connectStatus != MQTTConnected)
    {
      vTaskDelay(200);
    }
    else
    {
      MQTT_ReceiveLoop(&MQTT_Context);
    }
	}
}

void vNetWorkTransmit_Task( void * pvParameters )
{
	while(1)
	{
    if(MQTT_Context.connectStatus != MQTTConnected)
    {
      network_upline();
    }
    else
    {
      if(xQueueReceive(Route_EvewToNet_Qhd, &Route_to_net_pub_back, 150000) == pdTRUE)
      {
        Network_PbulishMsg(&Route_to_net_pub_back);
      }
      else
      {
        MQTT_Ping(&MQTT_Context);
      }
    }
  }
}
